
	/************************************************************************************************

		@API
	 	Factory: jsl.Array

		Constructors:
			a			Array					(array*)
			a			replicate			(n, v*)

		Slicers:
			a			after					(v, n*, idx*)
			a			before				(v, n*, idx*)
			v			last					()
			a			left					(n)
			a			right					(n)

		Info:
			b			contains			(v, idx*)
			n			index					(v, n*, idx*)
      b     isUnique    	()
			b			intersects		(a)

		Operations:
			n			count					(v, idx*)
			n			sum						(idx*)
			a			unique				(right*)
			a			intersection	(a)
			a			union					(a)
			a			less					(a)
			v			reduceRight		(rule, value*)

		Modifiers:
			a			inject				(v, idx*)
			a			include				(v, idx*)
			a			modify				(v, v2, n*, idx*)
			a			remove				(v, n*, idx*)
			a			removeAt			(idx, n*)

	************************************************************************************************/

  //-------------------------------------------------------------------------------
  jsl.Array = function(array) {
  	try {
  		var proto, rv
  		//!
	    if(arguments.length > 1) 	throw jsl.ParamError()
			if(arguments.length)			jsl.validate(array, "a", "array")//!

			if(arguments.length)
				if(array.__augmented) return array
				else									rv = array
			else
				rv = []

			proto = jsl.Array.prototype
    	for(var id in proto) rv[id] = proto[id]
			return rv
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
  }
  //-------------------------------------------------------------------------------
	jsl.Array.__name 							= "jsl.Array"
	jsl.Array.__augmented					= true
  jsl.Array.__native						= { replicate: true, __name: true, __augmented: true }
  //-------------------------------------------------------------------------------
	jsl.Array.prototype						= jsl.Object()
	jsl.Array.prototype.__name 		= "jsl.Array"
	jsl.Array.prototype.__native 	= jsl.Object.clone(jsl.Object.prototype.__native)
  //-------------------------------------------------------------------------------
	jsl.Array.replicate = function(n, v) {
		try {
			var rv

			//!
			if(!arguments.length || arguments.length > 2) throw jsl.ParamError()
			jsl.validate(n,"i+", "n")//!

			rv = Array(n)

			if(arguments.length === 2)
				for(var a = 0, l = rv.length; a < l; a++)
					rv[a] = v

			return rv
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.after = function(v, n, idx) {
		try {
			var i

			//!
			if(!jsl.isArray(this))												throw jsl.TypeError("BadMethodCall:Array")
			if(!arguments.length || arguments.length > 3) throw jsl.ParamError()
			if(arguments.length >= 2) 										jsl.validate(n	, "i!0"	, "n")
			if(arguments.length === 3) 										jsl.validate(idx, "i+"	, "idx")//!

			i = jsl.Array.prototype.index.apply(this, arguments)

			return i !== -1 ? this.slice(i + 1) : []
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.before = function(v, n, idx) {
		try {
			var i

			//!
      if(!jsl.isArray(this))												throw jsl.TypeError("BadMethodCall:Array")
			if(!arguments.length || arguments.length > 3) throw jsl.ParamError()
			if(arguments.length >= 2) 										jsl.validate(n	, "i!0"	, "n")
			if(arguments.length === 3) 										jsl.validate(idx, "i+"	, "idx")//!

			i = jsl.Array.prototype.index.apply(this, arguments)

			return i !== -1 ? this.slice(0, i) : this.slice()
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.last = function() {
		try {
			//!
      if(!jsl.isArray(this))	throw jsl.TypeError("BadMethodCall:Array")
			if(arguments.length) 		throw jsl.ParamError()//!


			return this.length ? this[this.length - 1] : null
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.left = function(n) {
		try {
			//!
      if(!jsl.isArray(this))			throw jsl.TypeError("BadMethodCall:Array")
			if(arguments.length !== 1) 	throw jsl.ParamError()
			jsl.validate(n, "i", "n")//!

			return this.slice(0, n)
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.right = function(n) {
		try {
			//!
      if(!jsl.isArray(this))			throw jsl.TypeError("BadMethodCall:Array")
			if(arguments.length !== 1) 	throw jsl.ParamError()
			jsl.validate(n, "i", "n")//!

			return n != 0 ? this.slice(-n) : []
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.contains = function(v, idx) {
		try {
			//!
      if(!jsl.isArray(this))												throw jsl.TypeError("BadMethodCall:Array")
			if(!arguments.length || arguments.length > 2) throw jsl.ParamError()
			if(arguments.length === 2) 										jsl.validate(idx, "i+"	, "idx")//!

			return jsl.Array.prototype.index.call(this, v, 1, idx || 0) !== -1
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.index = function(v, n, idx) {
		try {
			var i, ix, rv, a, l

			//!
      if(!jsl.isArray(this))												throw jsl.TypeError("BadMethodCall:Array")
			if(!arguments.length || arguments.length > 3) throw jsl.ParamError()
			if(arguments.length >= 2 && n != "*")					jsl.validate(n	, "i!0"	, "n")
			if(arguments.length === 3) 										jsl.validate(idx, "i+"	, "idx")//!

			i 	= +n		|| 1
			ix	= +idx	|| 0
			rv	= -1

			if(n == "*") {

				rv = []

				for(a = ix, l = this.length; a < l; a++)
					if(this[a] === v)
						rv.push(a)

				rv = rv.length ? rv : null
			}
			else if(i < 0) {
				for(a = this.length - 1; a >= ix && rv === -1; a--)
					if(this[a] === v && !++i)
						rv = a
			}
			else
				for(a = ix, l = this.length; a < l && rv === -1; a++)
					if(this[a] === v && !--i)
						rv = a

			return rv
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.isUnique = function() {
		try {
			//!
      if(!jsl.isArray(this))	throw jsl.TypeError("BadMethodCall:Array")
			if(arguments.length) 		throw jsl.ParamError()//!

			loop:
			for(var a = 0, l = this.length; a < l; a++)
				for(var b = a + 1; b < l; b++)
					if(this[a] === this[b])
						break loop;

			return a === l
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.intersects = function(a) {
		try {
			var i

			//!
      if(!jsl.isArray(this))			throw jsl.TypeError("BadMethodCall:Array")
			if(arguments.length !== 1) 	throw jsl.ParamError()
			jsl.validate(a, "al", "a")//!

			for(var b = 0, l = a.length; b < l && !i; b++)
				i	= jsl.Array.prototype.index.call(this, a[b]) !== -1

			return !!i
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.count = function(v, idx) {
		try {
			var n

			//!
      if(!jsl.isArray(this))												throw jsl.TypeError("BadMethodCall:Array")
			if(!arguments.length || arguments.length > 2) throw jsl.ParamError()
			if(arguments.length === 2) 										jsl.validate(idx, "i+"	, "idx")//!

			n = 0

			for(var a = +idx || 0, l = this.length; a < l; a++)
				if(this[a] === v)
					n++

			return n
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.sum = function(idx) {
		try {
			var n

			//!
      if(!jsl.isArray(this))		throw jsl.TypeError("BadMethodCall:Array")
			if(arguments.length > 1) 	throw jsl.ParamError()
			if(arguments.length)			jsl.validate(idx, "i+", "idx")//!

			n = 0

			for(var a = +idx || 0, l = this.length; a < l; a++)
				n += +this[a]

			return n
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.unique = function(right) {
		try {
			var a, l, v, i

			//!
      if(!jsl.isArray(this))		throw jsl.TypeError("BadMethodCall:Array")
			if(arguments.length > 1) 	throw jsl.ParamError()
			if(arguments.length)			jsl.validate(right, "b"	, "right")//!

			v 	= []
			i		= jsl.Array.prototype.index

			//right
			//-----------------------------------------------------------------------
			if(right) {
				for(a = this.length - 1; a >= 0; a--)
					if(i.call(v, this[a]) === -1)
						v.unshift(this[a])
			}
			//left
			//-----------------------------------------------------------------------
			else
				for(a = 0, l = this.length; a < l; a++)
					if(i.call(v, this[a]) === -1)
						v.push(this[a])

			return v
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.intersection = function(a) {
		try {
			var v

			//!
      if(!jsl.isArray(this))			throw jsl.TypeError("BadMethodCall:Array")
			if(arguments.length !== 1) 	throw jsl.ParamError()
			jsl.validate(a, "al", "a")//!

			v = []

			for(var b = 0, l = this.length; b < l; b++)
				for(var c = 0, l2 = a.length; c < l2; c++)
					if(a[c] === this[b]) {
						v.push(this[b])
						break;
					}

			return v
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.union = function(a) {
		try {
			var v, i, b, l

			//!
      if(!jsl.isArray(this))			throw jsl.TypeError("BadMethodCall:Array")
			if(arguments.length !== 1) 	throw jsl.ParamError()
			jsl.validate(a, "al", "a")//!

			v = this.slice()
			i	= jsl.Array.prototype.index

			for(b = 0, l = a.length; b < l; b++)
				if(i.call(v, a[b]) === -1)
					v.push(a[b])

			return v
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.less = function(a) {
		try {
			var v, i, ix, b

			//!
      if(!jsl.isArray(this))			throw jsl.TypeError("BadMethodCall:Array")
			if(arguments.length !== 1) 	throw jsl.ParamError()
			jsl.validate(a, "al", "a")//!

			v = this.slice()
			i	= jsl.Array.prototype.index

			for(b = a.length - 1; b >= 0; b--) {

				ix = i.call(v, a[b])
				if(ix !== -1)
					v.splice(ix, 1)
			}

			return v
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.inject = function(v, idx) {
		try {
			var args, i

			//!
      if(!jsl.isArray(this))												throw jsl.TypeError("BadMethodCall:Array")
			if(!arguments.length || arguments.length > 2) throw jsl.ParamError()
			if(arguments.length === 2)										jsl.validate(idx,"i+", "idx")//!

			i = arguments.length === 2 ? +idx : this.length

			if(v instanceof Array) {

				//@sys: Possible RangeError: Maximum call stack size exceeded (too many arguments)
				for(var a = 0, l = v.length; a < l; a += 20000) {

					args = [i + a * 20000, 0].concat(v.slice(a, a + 20000))
					this.splice.apply(this, args)
				}
			}
			else
				this.splice(i, 0, v)

			return this
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.include = function(v, idx) {
		try {
			var i, ix

			//!
      if(!jsl.isArray(this))												throw jsl.TypeError("BadMethodCall:Array")
			if(!arguments.length || arguments.length > 2) throw jsl.ParamError()
			if(arguments.length === 2)										jsl.validate(idx,"i+", "idx")//!

			i 	= arguments.length === 2 ? +idx : this.length
			ix	= jsl.Array.prototype.index

			if(v instanceof Array) {

				for(var a = 0, l = v.length; a < l; a++)
					if(ix.call(this, v[a]) === -1)
						this.splice(i++, 0, v[a])
			}
			else if(ix.call(this, v) === -1)
				this.splice(i, 0, v)

			return this
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.modify = function(v, v2, n, idx) {
		try {
			var ix, ind, i

			//!
      if(!jsl.isArray(this))														throw jsl.TypeError("BadMethodCall:Array")
			if(arguments.length < 2 || arguments.length > 4) 	throw jsl.ParamError()
			if(arguments.length >= 3 && n != "*") 						jsl.validate(n	, "i!0"	, "n")
			if(arguments.length === 4)												jsl.validate(idx, "i+"	, "idx")//!

			ix 	= arguments.length === 4 ? +idx : 0
			ind	= jsl.Array.prototype.index

			if(n == "*") {

				for(var a = this.length - 1; a >= ix; a--)
					if(this[a] === v)
						this.splice(a, 1, v2)
			}
			else {

				i = arguments.length >= 3 ? +n : 1
				i = ind.call(this, v, i, ix)

				if(i !== -1)
					this.splice(i, 1, v2)
			}

			return this
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.remove = function(v, n, idx) {
		try {
			var ix, ind, i

			//!
      if(!jsl.isArray(this))												throw jsl.TypeError("BadMethodCall:Array")
			if(!arguments.length || arguments.length > 3)	throw jsl.ParamError()
			if(arguments.length >= 2 && n != "*") 				jsl.validate(n	, "i!0"	, "n")
			if(arguments.length === 3)										jsl.validate(idx, "i+"	, "idx")//!

			ix 	= +idx || 0
			ind	= jsl.Array.prototype.index

			if(n == "*") {

				for(var a = this.length - 1; a >= ix; a--)
					if(this[a] === v)
						this.splice(a, 1)
			}
			else {

				i = +n || 1
				i = ind.call(this, v, i, ix)

				if(i !== -1)
					this.splice(i, 1)
			}

			return this
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
	jsl.Array.prototype.removeAt = function(idx, n) {
		try {
			//!
      if(!jsl.isArray(this))												throw jsl.TypeError("BadMethodCall:Array")
			if(!arguments.length || arguments.length > 2) throw jsl.ParamError()
			jsl.validate(idx, "i+", "idx")
			if(arguments.length === 2)										jsl.validate(n,"i+", "n")//!

			this.splice(idx, arguments.length === 2 ? +n : 1)
			return this
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//overriding
	//-------------------------------------------------------------------------------
	jsl.Array.prototype.walk = function(action, obj) {
		try {
			var v, a, l//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			jsl.validate(action, "f", "action")
			if(arguments.length === 2)	jsl.validate(obj, "o", "obj")//!

			try {
				for(a = 0, l = this.length; a < (l <= this.length ? l : this.length); a++)
					if((v = obj ? action.call(obj, this[a], a, this)
											: action(this[a], a, this)) !== undefined)
						break
			}
			catch(e) {
				throw jsl.ParamError("BadScript:action", e)
			}

			return v
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
  //-------------------------------------------------------------------------------
  jsl.Array.prototype.iterator = function() {
    try {
    	var it, obj//!
      if(arguments.length)	throw jsl.ParamError()//!

			it	= { __name: "ArrayIterator", __ix: 0, __l: this.length }
    	obj	= this

  		//-------------------------------------------------------------------------------
			it.hasNext = function() {
				try {//!
					if(arguments.length)	throw jsl.ParamError()//!

					this.__l = this.__l <= obj.length ? this.__l : obj.length
					return this.__ix < this.__l
				}
				catch(e) {
					jsl.throwError(e, this, arguments)
				}
			}
  		//-------------------------------------------------------------------------------
			it.next = function() {
				try {//!
					if(arguments.length) 	throw jsl.ParamError()
					if(!this.hasNext())		throw jsl.EndOfIterationError()//!

					return obj[this.__ix++]
				}
				catch(e) {
					jsl.throwError(e, this, arguments)
				}
			}

      return it
    }
    catch(e) {
      jsl.throwError(e, this, arguments)
    }
  }
	//-------------------------------------------------------------------------------
	jsl.Array.prototype.map = function(rule, obj) {
		try {
			var obj2//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			jsl.validate(rule, "f", "rule")
			if(arguments.length === 2)	jsl.validate(obj, "o", "obj")//!

			obj2 = []
			jsl.Array.prototype.walk.call(this, function(v, id, obj) {
				obj2[id] = rule.apply(this, arguments)
			}, obj || jsl.__go)

			return obj2
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.Array.prototype.filter = function(rule, obj) {
		try {
			var obj2//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			jsl.validate(rule, "f", "rule")
			if(arguments.length === 2)	jsl.validate(obj, "o", "obj")//!

			obj2 = []
			jsl.Array.prototype.walk.call(this, function(v, id, obj) {
				if(rule.apply(this, arguments)) obj2[id] = v
			}, obj || jsl.__go)

			return obj2
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.Array.prototype.some = function(rule, obj) {
		try {
			var b//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			jsl.validate(rule, "f", "rule")
			if(arguments.length === 2)	jsl.validate(obj, "o", "obj")//!

			b = jsl.Array.prototype.walk.call(this, function(v, id, obj) {
				if(rule.apply(this, arguments)) return true
			}, obj || jsl.__go)

			return !!b
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.Array.prototype.every = function(rule, obj) {
		try {
			var b//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			jsl.validate(rule, "f", "rule")
			if(arguments.length === 2)	jsl.validate(obj, "o", "obj")//!

			b = jsl.Array.prototype.walk.call(this, function(v, id, obj) {
				if(!rule.apply(this, arguments)) return false
			}, obj || jsl.__go)

			return b == null || b
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.Array.prototype.reduce = function(rule, value) {
		try {
			var acc, i, v0//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			jsl.validate(rule, "f", "rule")//!

			i		= 0
			v0	= arguments.length === 2
			acc = value

			jsl.Array.prototype.walk.call(this, function(v, id, obj) {

				if(v0)			acc = rule(acc, v, id, obj)
				else if(i)	acc = rule(acc, v, id, obj)
				else				acc = v

				i++
			})

			//!
			if(!i && arguments.length === 1)	throw jsl.MisuseError()//!
			return acc
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}
	//-------------------------------------------------------------------------------
	jsl.Array.prototype.reduceRight = function(rule, value) {
		try {
			var acc, a//!
			if(!arguments.length || arguments.length > 2)	throw jsl.ParamError()
			jsl.validate(rule, "f", "rule")//!

			a 	= arguments.length === 2 ? this.length - 1 	: this.length - 2
			acc = arguments.length === 2 ? value						:	this[this.length - 1]

			if(a >= 0)
				try {
					for(; a >= 0; a--)
						acc = rule(acc, this[a], a, this)
				}
				catch(e) {
					throw jsl.ParamError("BadScript:action", e)
				}

			//!
			if(!this.length && arguments.length === 1)	throw jsl.MisuseError()//!
			return acc
		}
		catch(e) {
			jsl.throwError(e, this, arguments)
		}
	}

	//Array init
	//-------------------------------------------------------------------------------
	;(function() {
		var id, prt, Ary

		Ary = jsl.Array
		prt	= Ary.prototype

		for(id in prt) {

			Ary.__native[id] 	= true
			prt.__native[id]	= true

			if(typeof prt[id] === "function" && !prt[id].__name) {

				Ary[id] = function(obj) {
					try {//!
						if(!arguments.length)	throw jsl.ParamError()
						if(obj == null)				throw jsl.ParamError("NullValue:obj")//!

						return arguments.callee.__f.apply(obj, Array.prototype.slice.call(arguments, 1))
					}
					catch(e) {
						jsl.throwError(e, this, arguments)
					}
				}

				Ary[id].__name	= prt[id].__name = id
				Ary[id].__f			= prt[id]
			}
		}
	})()
